/* -*-c++-*- */
/* osgGIS - GIS Library for OpenSceneGraph
 * Copyright 2007-2008 Glenn Waldron and Pelican Ventures, Inc.
 * http://osggis.org
 *
 * osgGIS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#ifndef _OSGGISPROJECTS_MAP_LAYER_COMPILER_
#define _OSGGISPROJECTS_MAP_LAYER_COMPILER_ 1

#include <osgGISProjects/Common>
#include <osgGISProjects/MapLayer>
#include <osgGISProjects/QuadKey>
#include <osgGISProjects/CellCompiler>
#include <osgGISProjects/Cell>
#include <osgGIS/ResourcePackager>
#include <osgGIS/TaskManager>
#include <osgGIS/SmartReadCallback>
#include <osgGIS/FeatureLayerCompiler>
#include <osgDB/Archive>

using namespace osgGIS;

namespace osgGISProjects
{
    class OSGGISPROJECTS_EXPORT Profile : public osg::Referenced
    {
    public:
        Profile() { }
    };
   

    class OSGGISPROJECTS_EXPORT CompileSession : public osg::Referenced
    {
    public:
        virtual osg::ref_ptr<Task> getNextCompletedTask() =0;

        virtual double getElapsedTimeSeconds() const =0;
        
        virtual double getPredicitedRemainingTimeSeconds() const =0;

        virtual osg::Group* getOrCreateSceneGraph() =0;
    };


    /**
     * A callback that returns a value indicating whether to compile
     * the cell in question.
     */
    class OSGGISPROJECTS_EXPORT CellSelector : public osg::Referenced
    {
    public:
        virtual bool selectCell( Cell* cell ) const =0; //const std::string& cell_id ) const =0; 
    };


    /**
     * Compiles scene graphs from MapLayer definitions.
     */
    class OSGGISPROJECTS_EXPORT MapLayerCompiler : public osg::Referenced
    {
    public:
        /**
         * Constructs a new map layer compiler.
         *
         * @param map_layer
         *      Layer that we are going to compile.
         *
         * @param session
         *      Session under which to compiler the map layer
         */
        MapLayerCompiler( MapLayer* map_layer, Session* session =NULL );

        /**
         * Gets the map layer that this compiler is meant to compile.
         *
         * @return Map layer.
         */
        MapLayer* getMapLayer() const;

        /**
         * Sets whether the map layer is to be compiled into paged geometry.
         *
         * @param paged
         *      True to compile into paged cells, false for non-paged cells.
         */
        void setPaged( bool paged );

        /**
         * Gets whether to build a paged dataset.
         *
         * @return True if the map cells will be paged; false if not
         */
        bool getPaged() const;

        /**
         * Sets the absolute URI or pathname of the compiler output. This value will be
         * used as a template for creating output names for paged data cells as well as
         * localized resource files (textures, external models).
         *
         * This value is required if either the Paged of the CopyResourcesToOutputLocation
         * property is set to true. 
         *
         * @param uri
         *      Absolute URI/pathname
         */
        void setAbsoluteOutputURI( const std::string& uri );

        /**
         * Assigns an optional resource packager to the compiler. The resource packager will
         * prepare referenced resources (like texture skins and external models) and copy them
         * to the output location.
         *
         * @param packager
         *      Packager to use with this compiler.
         */
        void setResourcePackager( ResourcePackager* packager );

        /**
         * Gets the packager that will deploy resources and nodes for this compiler.
         */
        ResourcePackager* getResourcePackager() const;
                
        /** 
         * Sets the reference terrain against which to compile.
         */
        void setTerrain(
            osg::Node*              terrain, 
            const SpatialReference* terrain_srs =NULL,
            const GeoExtent&        terrain_extent =GeoExtent::infinite() );
            
        /**
         * Gets the scene graph holding the reference terrain, if set.
         */
        osg::Node* getTerrainNode();
        
        /**
         * Gets the spatial reference system of the reference terrain, if set.
         */
        SpatialReference* getTerrainSRS() const;
        
        /**
         * Gets the geospatial extents of the reference terrain, if set.
         */
        const GeoExtent& getTerrainExtent() const;
            
        /**
         * Sets the archive to which the compiler should write files,
         * if applicable.
         */
        void setArchive( osgDB::Archive* archive, const std::string& filename );
        
        /**
         * Gets the archive to which the compiler should write files, if set.
         */
        osgDB::Archive* getArchive();
        const std::string& getArchiveFileName() const;
            
        /**
         * Gets the session under which this compiler will operate.
         *
         * @return A session
         */
        Session* getSession();

        /**
         * Gets the spatial reference system of the output data that this
         * compiler will generate.
         *
         * @return A spatial reference, or NULL if it cannot be determined.
         */
        osgGIS::SpatialReference* getOutputSRS();

        /**
         * Sets a callback that the compiler will use to determine whether
         * to queue a particular cell for compilation.
         *
         * @param selector
         *      Selector callback
         */
        void setCellSelector( CellSelector* selector );

        /**
         * For multi-level map layers, whether to build all LODs in a cell before
         * moving on to the next cell.
         *
         * @param value
         *       True to build map layer levels depth-first; false to build
         *      map layers breadth first (i.e. build all level 0 cells before moving
         *      on to level 1.
         */
        void setBuildDepthFirst( bool value );

    public:
        /**
         * Compiles the entire cell graph.
         *
         * @param task_man
         *      Task manager to employ for parallel/distributed compilation
         *
         * @return
         *      True if the compilation succeeded, false if it failed.
         */
        virtual osg::Group* compile( TaskManager* task_man =NULL);

        virtual osg::Group* compileIndexOnly( CompileSession* session );



        virtual CompileSession* startCompiling( TaskManager* task_man =NULL );

        virtual bool continueCompiling( CompileSession* session );

        virtual bool finishCompiling( CompileSession* session );

        

    public:

        virtual Profile* createProfile() =0;

        /**
         * Creates a cursor that you can use to iterate over all the cells comprising
         * the map layer under the specified profile.
         *
         * @return
         *      A cell cursor. Caller is responsible for deleting the return object.
         */
        virtual CellCursor* createCellCursor( Profile* profile ) =0;

    public:

        std::string createAbsPathFromTemplate( const std::string& core );
        std::string createRelPathFromTemplate( const std::string& core );

    protected:

        //virtual void collectCells( Profile* ) =0;
        virtual unsigned int queueTasks( Profile*, TaskManager* ) =0;
        virtual void buildIndex( Profile*, osg::Group* ) =0;
        virtual void processCompletedTask( CellCompiler* ) { }

    protected:
        osg::ref_ptr<MapLayer>          map_layer;

        bool                            paged;
        std::string                     output_uri;
        osg::ref_ptr<ResourcePackager>  resource_packager;

        osg::ref_ptr<osgDB::Archive>    archive;
        std::string                     archive_filename;

        osg::ref_ptr<osg::Node>         terrain_node;
        osg::ref_ptr<SpatialReference>  terrain_srs;
        GeoExtent                       terrain_extent;

        osg::ref_ptr<Session>           session;

        osg::ref_ptr<CellSelector>      cell_selector;
        bool                            depth_first;

        void setCenterAndRadius( osg::Node* plod_or_proxy, const GeoExtent& cell_extent, SmartReadCallback* reader );
    };
}

#endif // _OSGGISPROJECTS_MAP_LAYER_COMPILER_

